﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using BakalarskaPraca.Generation.Helpers;
using ColladaDotNet;
using ColladaDotNet.Geometry;
using ColladaDotNet.Scene;

namespace BakalarskaPraca.Generation.Data
{
    public class PlantModel
    {
        private static Dictionary<String, PlantModel> _usedModels = new Dictionary<string, PlantModel>(); 
        private readonly List<Geometry> _geometries;
        private readonly List<Node> _nodes; 
        private readonly ColladaDocument _colladaDocument;
        private readonly XmlDocument _xmlDocument;
        private readonly string _requirementsDirPath;
        public readonly string Name;

        public static PlantModel GetPlantModel(PlantData data, ColladaDocument document)
        {
            PlantModel result;
            lock (_usedModels)
            {
                if (!_usedModels.TryGetValue(data.Name, out result))
                {
                    result = new PlantModel(data, document);
                    result.InsertDepenedenciesToDocument(document);
                    _usedModels.Add(data.Name, result);
                }
            }
            return result;
        }

        private PlantModel(PlantData data, ColladaDocument document)
        {
            if (!File.Exists(data.ModelPath))
            {
                throw new FileNotFoundException("Plant model not found in: " + data.ModelPath);
            }
            this._requirementsDirPath = data.RequirementsDirPath;
            this.Name = data.Name;
            this._colladaDocument = ColladaDocument.FromFile(data.ModelPath);
            this._xmlDocument = this._colladaDocument.Geometries[0].XmlElement.OwnerDocument;
            this._geometries = this._colladaDocument.Geometries;
            this._nodes = this._colladaDocument.VisualScenes[0].Nodes;
            
        }

        private void InsertDepenedenciesToDocument(ColladaDocument document)
        {
            XmlNode geometryLibrary = document.Geometries[0].XmlElement.ParentNode;
            if (geometryLibrary == null || geometryLibrary.OwnerDocument == null)
            {
                throw new ArgumentException("Geometry library is empty");
            }
            XmlDocument xmlDocument = geometryLibrary.OwnerDocument;

            XmlHelper.MergeXmlNodes(this._xmlDocument, xmlDocument, "library_nodes");
            XmlHelper.MergeXmlNodes(this._xmlDocument, xmlDocument, "library_geometries");
            XmlHelper.MergeXmlNodes(this._xmlDocument, xmlDocument, "library_materials");
            XmlHelper.MergeXmlNodes(this._xmlDocument, xmlDocument, "library_effects");
            XmlHelper.MergeXmlNodes(this._xmlDocument, xmlDocument, "library_images");

            XmlNodeList list = xmlDocument.GetElementsByTagName("library_nodes");
            XmlNode nodesLibrary;
            if (list.Count == 0)
            {
                nodesLibrary = xmlDocument.CreateNode(XmlNodeType.Element, "library_nodes", "");
                xmlDocument.DocumentElement.AppendChild(nodesLibrary);
            } 
            else
            {
                nodesLibrary = list[0];
            }
            if (nodesLibrary.OwnerDocument == null)
            {
                throw new ArgumentException("Nodes library documnet does not exist");
            }
            /*this._geometries.ForEach(g =>
                                         {
                                             g.Id = this.Name + "_" + g.Id;
                                             g.PreSave();
                                             geometryLibrary.AppendChild(
                                                 geometryLibrary.OwnerDocument.ImportNode(g.XmlElement, true));
                                         });
            */
            XElement elem = new XElement("node");
            elem.SetAttributeValue("type", "NODE");
            elem.SetAttributeValue("id", this.Name);

            this._nodes.ForEach(node =>
                                    {
                                        node.Id = this.Name + "_" + node.Name + node.Id;
                                        node.PreSave();
                                        nodesLibrary.AppendChild(
                                            nodesLibrary.OwnerDocument.ImportNode(node.XmlElement, true));
                                        XElement nodeInstance = new XElement("instance_node");
                                        nodeInstance.SetAttributeValue("url", "#" + node.Id);
                                        elem.Add(nodeInstance);
                                    });

            XmlReader reader = elem.CreateReader();
            XmlDocument doc2 = new XmlDocument();
            doc2.Load(reader);

            nodesLibrary.AppendChild(xmlDocument.ImportNode(doc2.DocumentElement, true));
        }

        public static void ClearData()
        {
            _usedModels.Clear();
        }

        public static List<string> GetPlantRequirements()
        {
            return _usedModels.Values.Select(data => data._requirementsDirPath).ToList();
        }
    }
}
